package nachos.vm;

import java.util.LinkedList;

import nachos.machine.Lib;
import nachos.machine.Machine;
import nachos.machine.OpenFile;
import nachos.machine.Processor;
import nachos.threads.Lock;
import nachos.threads.ThreadedKernel;

public class Swap {

	private static final String SWAP_FILE_NAME = "genliu.swap";
	private OpenFile swapFile;
	
	private Lock lock;
	private byte[] phyMem;
	private int phyPageSize = Processor.pageSize;
	
	/**
	 * keep track of free swap page numbers
	 */
	private LinkedList<Integer> freeSPNList;
	
	/**
	 * you should first refer to freeSPNList to check if any free spn available<p>
	 * if not, use currentSPN and then increase it
	 */
	private int currentSPN = 0;
	
	public Swap() {
		lock = new Lock();
		phyMem = Machine.processor().getMemory();
		freeSPNList = new LinkedList<Integer>();
		swapFile = ThreadedKernel.fileSystem.open(SWAP_FILE_NAME, true);
	}
	
	public void cleanUp() {
		swapFile.close();
		ThreadedKernel.fileSystem.remove(SWAP_FILE_NAME);
	}
	
	/**
	 * save one phy page to swap space
	 * if page is already in the swap, pass the original spn, write page to original swap page
	 * if not, pass -1 as spn, get the new spn by return value
	 * @param ppn 
	 * @param spn 
	 * @return spn which store the phy page
	 */
	public int saveToSwap(int ppn, int spn) {
		
		if (spn == -1) {
			// allocate spn
			lock.acquire();
			if (!freeSPNList.isEmpty()) {
				spn = freeSPNList.removeFirst();
			} else {
				spn = currentSPN++;
			}
			lock.release();
		}
		
		int paddr = ppn * phyPageSize;
		int saddr = spn * phyPageSize;
		
		int result = swapFile.write(saddr, phyMem, paddr, phyPageSize);
		//Lib.assertTrue(result == phyPageSize, "saveToSwap fail, actual bytes writen:"+result);
		
		Lib.debug(debugFlag, "Save ppn "+ppn+" to swap pos "+spn);
		return spn;
	}
	
	/**
	 * restore a phy page from swap and remove the swap page
	 * @param ppn restore to
	 * @param spn store the phy page
	 */
	public void restoreFromSwap(int ppn, int spn) {
		Lib.assertTrue(spn!=-1, "restoreFromSwap error");
		int paddr = ppn * phyPageSize;
		int saddr = spn * phyPageSize;
		
		int result = swapFile.read(saddr, phyMem, paddr, phyPageSize);
		//Lib.assertTrue(result == phyPageSize, "restoreFromSwap fail, actual bytes read:"+result);
		
		Lib.debug(debugFlag, "restore ppn "+ppn+" from swap pos "+spn);
	}
	
	/**
	 * when process finishes, call this to free swap page
	 * @param spn
	 */
	public void freeSwapPage(int spn) {
		if (spn==-1) return;
		lock.acquire();
		freeSPNList.add(spn);
		lock.release();
	}
	
	private static final char debugFlag = 'a';
}
